using System.Collections.Generic;
using UnityEngine;

namespace ai_library
{
	public class EntityException : System.Exception
	{
		protected string message;
		public EntityException(string message) { this.message = message; }
		public override string Message { get { return message; } }
		public override string ToString() { return "Entity Exception: " + message; }
	}

	public abstract class Entity : IEntity
	{
		private static int nextId = 0;

		private readonly int m_id;

		public Entity(Transform trans, Vector2 velocity, 
		              float mass, float maxSpeed, float maxForce, float maxTurnRate, float bRadius)
		{
			EnableDebugSteeringHelpers = false;

			Feelers = new List<Vector2>();

			m_id = nextId;
			++nextId;

			Transformation = trans;

			Position = new Vector2 (trans.position.x, trans.position.y);
			Forward = new Vector2 (trans.forward.x, trans.forward.y);
			Right = new Vector2(Forward.y, -Forward.x);
			Left = new Vector2(-Forward.y, Forward.x);

			Mass = mass;
			Velocity = velocity;
			MaxSpeed = maxSpeed;
			MaxForce = maxForce;
			MaxTurnRate = maxTurnRate;
			BRadius = bRadius;

			Steering = new SteeringBehavior(this);
			Steering.SetSummingMethod(SteeringBehavior.SummingMethod.prioritized);
		}

		public int ID
		{
			get
			{
				return m_id;
			}
		}

		public SteeringBehavior Steering
		{
			get;
			private set;
		}

		public Transform Transformation
		{
			get; set;
		}

		public Vector2 Position
		{
			get; set;
		}
		
		public Vector2 Forward
		{
			get; set;
		}

		public Vector2 Left
		{
			get; set;
		}

		public Vector2 Right
		{
			get; set;
		}
		
		public float Mass
		{
			get; set;
		}
		
		public float MaxSpeed
		{
			get; set;
		}
		
		public float MaxForce
		{
			get; set;
		}
		
		public float MaxTurnRate
		{
			get; set;
		}

		public Vector2 Velocity
		{
			get; set;
		}

		public float TimeElapsed
		{
			get;
			private set;
		}

		public float BRadius
		{
			get;
			private set;
		}

		public bool EnableDebugSteeringHelpers
		{
			get;
			set;
		}

		public bool EnableDebugFeelerHelpers
		{
			get;
			set;
		}

		public List<Vector2> Feelers
		{
			get;
			set;
		}

		public virtual void Update(float dt)
		{
			TimeElapsed = dt;

			Vector2 steeringForce = Steering.Calculate();

			Vector2 acceleration = steeringForce / Mass;

			Velocity += acceleration * TimeElapsed;

			if (Velocity.magnitude > MaxSpeed)
			{
				Velocity = Velocity.normalized * MaxSpeed;
			}

			Position += Velocity * TimeElapsed;

			if (Velocity.magnitude > 0.0f)
			{
				Forward = Velocity.normalized;
				Right = new Vector2(Forward.y, -Forward.x);
				Left = new Vector2(-Forward.y, Forward.x);
			}

			Transformation.position = Position;
			Transformation.forward = Forward;
			Transformation.right = Right;


			// Draw helpers
			if (EnableDebugSteeringHelpers)
			{
				Debug.DrawLine(Transformation.position, Transformation.position + (new Vector3(steeringForce.x, steeringForce.y, 0.0f) * 2), Color.yellow);
				Debug.DrawLine(Transformation.position, Transformation.position + (new Vector3(Forward.x, Forward.y, 0.0f) * 2), Color.red);
				Debug.DrawLine(Transformation.position, Transformation.position + (new Vector3(Right.x, Right.y, 0.0f) * 2), Color.blue);
				Debug.DrawLine(Transformation.position, Transformation.position + (new Vector3(Left.x, Left.y, 0.0f) * 2), Color.green);
			}

			if (EnableDebugFeelerHelpers)
			{
				foreach (Vector2 feeler in Feelers)
				{
					Debug.DrawLine(Transformation.position, Transformation.position + new Vector3(feeler.x, feeler.y, 0.0f), Color.white);
				}
			}
		}

		public abstract bool ProcessMessage(Message m);

		private void SendMessage(int receiverId, MessageTypes message, float delay)
		{
			try
			{
				MessageDispatcher.Instance.SendMessage(new Message(ID, receiverId, message, delay));
			}
			catch (MessageDispatcherException e)
			{
				throw new EntityException(e.ToString());
			}
		}
	}
}
